/*
 * Copyright 2010-2011 Clavid AG
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.clavid.example.oauth.suisseid;

import java.io.IOException;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;


/**
 * This filter can be used to protect web resources such as JSP pages.
 * <p>
 * @version $Id: ClavidOAuthSuisseIDFilter.java 18 2011-10-22 16:54:39Z telbie $
 */
public class ClavidOAuthSuisseIDFilter implements Filter {

  private static final String SESSION_ATTRIBUTE_OAUTH_TOKEN_SECRET = "oauth_token_secret";
  
  private String consumerKey = null;
  private String consumerSecret = null;
  private String oauthCallback = null;
  private String userLanguage = null;
  private String errorPage = null;
  private List<String> requiredAttributes = new ArrayList<String>();
  private List<String> optionalAttributes = new ArrayList<String>();

  public void init(FilterConfig config) throws ServletException {
    consumerKey = getConfigParameter(config, "consumer-key", null);
    consumerSecret = getConfigParameter(config, "consumer-secret", null);
    oauthCallback = getConfigParameter(config, "oauth-callback", null); 
    userLanguage = getConfigParameter(config, "user-language", null);
    errorPage = getConfigParameter(config, "error-page", null);
    for(int i = 0; i < 99; i++) {
      String val = getConfigParameter(config, "required-attribute-" + i, null);
      if (val != null) {
        requiredAttributes.add(val);
      }
      val = getConfigParameter(config, "optional-attribute-" + i, null);
      if (val != null) {
        optionalAttributes.add(val);
      }
    }
  }
  
  protected static String getConfigParameter(FilterConfig config, String parameter, String defaultValue) {
    String result = System.getProperty(parameter);
    if (result != null) {
      result = result.trim();
      if (result.length() <= 0) {
        result = null;
      }
    }
    if (result == null) {
      result = config.getInitParameter(parameter);
      if (result != null) {
        result = result.trim();
        if (result.length() <= 0) {
          result = null;
        }
      }
    }
    if (result == null) {
      result = defaultValue;
    }
    return result;
  }
  
  public void destroy() {
    // TODO Auto-generated method stub
    
  }

  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    // TODO Auto-generated method stub
    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;
    response.addHeader("Cache-Control", "no-cache");
    response.addHeader("Pragma", "no-cache");
    response.addDateHeader("Expires", 0);
    response.setContentType("text/html");
    try {
      HttpSession session = request.getSession();

      String command = request.getParameter("suisseid");
      if ("logout".equalsIgnoreCase(command)) {
        session.removeAttribute(ClavidOAuthSuisseIDBean.class.getName());
      }
      
      String token = request.getParameter(ClavidOAuthSuisseID.OAUTH_TOKEN);
      if (token != null) {
        token = token.trim();
        if (token.length() <= 0) {
          token = null;
        }
      }
      String tokenSecret = (String) session.getAttribute(SESSION_ATTRIBUTE_OAUTH_TOKEN_SECRET);
      if (tokenSecret != null) {
        session.removeAttribute(SESSION_ATTRIBUTE_OAUTH_TOKEN_SECRET);
        tokenSecret = tokenSecret.trim();
        if (tokenSecret.length() <= 0) {
          tokenSecret = null;
        }
      }
      String callback = oauthCallback;
      if (callback == null) {
        callback = request.getRequestURL().toString();
      }
      
      ClavidOAuthSuisseIDBean bean = (ClavidOAuthSuisseIDBean) session.getAttribute(ClavidOAuthSuisseIDBean.class.getName());
      if (bean == null) {
        if (token != null && tokenSecret != null) {
          ClavidOAuthSuisseID oauth = new ClavidOAuthSuisseID(consumerKey, consumerSecret, token, tokenSecret);
          oauth.callAccessToken();
          String number = oauth.getAttribute(ClavidOAuthSuisseID.SUISSE_ID_NUMBER);
          String name = oauth.getAttribute(ClavidOAuthSuisseID.SUISSE_ID_NAME);
          String email = oauth.getAttribute(ClavidOAuthSuisseID.SUISSE_ID_EMAIL);
          if (number != null || name != null || email != null) {
            bean = new ClavidOAuthSuisseIDBean();
            bean.setNumber(oauth.getAttribute(ClavidOAuthSuisseID.SUISSE_ID_NUMBER));
            bean.setEmail(oauth.getAttribute(ClavidOAuthSuisseID.SUISSE_ID_EMAIL));
            bean.setName(oauth.getAttribute(ClavidOAuthSuisseID.SUISSE_ID_NAME));
            session.setAttribute(ClavidOAuthSuisseIDBean.class.getName(), bean);
          } else {
            throw new Exception("no SuisseID attributes received");
          }
        } else {
          
          ClavidOAuthSuisseID oauth = new ClavidOAuthSuisseID(consumerKey, consumerSecret, callback);
          Locale locale = null;
          if (userLanguage != null) {
            locale = new Locale(userLanguage);
          }
          if (locale == null) {
            locale = request.getLocale();
          }
          if (locale == null) {
            locale = Locale.ENGLISH;
          }
          oauth.setUserLanguage(locale);
          for(String entry : requiredAttributes ) {
            oauth.setRequiredAttribute(entry);
          }
          for(String entry : optionalAttributes ) {
            oauth.setOptionalAttribute(entry);
          }
          
          String authorizationURL = oauth.getAuthorizationUrl();
          session.setAttribute(SESSION_ATTRIBUTE_OAUTH_TOKEN_SECRET, oauth.getTokenSecret());
          
          response.sendRedirect(authorizationURL);
          return;
        }
        
      }

      if (bean != null) {
        if (token != null) {
          response.sendRedirect(request.getRequestURL().toString());
        } else {
          checkAccess(request, response, bean);
          session.setAttribute(ClavidOAuthSuisseIDBean.SESSION_ATTRIBUTE_NAME, bean);
          chain.doFilter(request, response);    
        }
      } else {
        throw new Exception("SuisseID authentication failed");
      }
    } catch (Exception e) {
      e.printStackTrace();
      if (errorPage != null) {
        request.setAttribute("suisseid-error", e.getMessage());
        request.getRequestDispatcher(errorPage).forward(request, response);
      } else {
        response.sendError(401, "exception happened, " + e.getMessage());
      }
    }

  }

  protected void checkAccess(HttpServletRequest request, HttpServletResponse response, ClavidOAuthSuisseIDBean bean) throws AccessControlException {
    // can be overwritten
  }
  
}
